#ifndef PAGE_TABLE_H
#define PAGE_TABLE_H

#include <Types.h>
#include <BuddyAllocator.h>
#include <asm.h>


#define PAGE_TABLE_SIZE		1024
#define KERNEL_PAGE		0

#define PG_DIR_SHIFT	22
#define PG_DIR_MASK		0x3FFFFF
#define PG_TLB_SHIFT	12
#define PG_TLB_MASK	0x3FF000
#define PG_OFFSET_MASK	0xFFF

#define _pgd_i(addr)	\
	((addr) >> PG_DIR_SHIFT)

#define _pmd_i(addr)	\
	(((addr) & PG_DIR_MASK) >> PG_TLB_SHIFT)


#define NOT_PRESENT		0
#define USER_PAGE		0

#define PG_ADDR_MASK	0xFFF
class PageTable{
	page_t	pte[PAGE_TABLE_SIZE];
	public:
	PageTable(){}
	PageTable(PageTable &from_gpd, uint32 flag){
		if(!flag){
			//memcpy(&from_gpd, pte, 4096);
			uint32 i;
			page_t *pde_from = from_gpd.getPte(), *pde_to;
			for(i = _pgd_i(0xC0000000); i < PAGE_TABLE_SIZE; ++i){
				if(pde_from[i] > 0){
					pde_to = (page_t *) new PageTable();
					memcpy((void *)(pde_from[i] & ~PG_ADDR_MASK), (void *)pde_to, 4096);
					pte[i] = (pde_from[i] & PG_ADDR_MASK) | (addr_t) pde_to;
				}
			}
		}
	}
	
	page_t * getPte(){
		return pte;
	}
	
	/*Разметить страницу в таблице страниц процесса, но не выделять под нее фрейм*/
	void mapPageToTable(void *page, uint32 flags){
		uint32 PDE_idx = (addr_t)page >> PG_DIR_SHIFT;
		uint32 PTE_idx = ((addr_t)page & PG_TLB_MASK) >> PG_TLB_SHIFT;
		PageTable *pt = (PageTable *)(pte[PDE_idx]);
		if(!pt) pt = new PageTable;
		pt->pte[PTE_idx] = NOT_PRESENT;
	}
	/*Разметить страницу в таблице страниц процесса и выделить под нее фрейм*/
	void mapPageToFrame(void *page, uint32 flags){
		uint32 PDE_idx = (addr_t)page >> PG_DIR_SHIFT;
		uint32 PTE_idx = ((addr_t)page & PG_TLB_MASK) >> PG_TLB_SHIFT;
		PageTable *pt = (PageTable *)(pte[PDE_idx]);
		if(!pt) pt = new PageTable;
		pt->pte[PTE_idx] = (uint32)zone_normal->allocFrame(USER_PAGE | flags);
	}
	void * operator new(size_t size){
		return zone_normal->allocFrame(KERNEL_PAGE);
	}
	
	void enable_paging(addr_t gpd){
		asm(".code32 \
			movl %0, %%cr3;"::"r"(gpd):"%eax");
	
		asm(".code32 \
			mov %cr0, %eax;	\
 			bts	$31, %eax; 	\
			mov %eax, %cr0;");
	
	}
};

extern PageTable *globalPageDir;

#endif /* PAGE_TABLE_H */

